package com.example.miniodemo.controller;

import com.example.miniodemo.dto.FileDto;
import com.example.miniodemo.dto.UploadDto;
import com.example.miniodemo.entity.FileEntity;
import com.example.miniodemo.enums.FileStateEnum;
import com.example.miniodemo.service.FileService;
import com.example.miniodemo.service.OssService;
import com.example.miniodemo.vo.FileVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.extension.api.R;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Chen Lin
 * @date 2022.03.21  4:56 PM
 * @description
 */
@RestController
@RequestMapping("/upload")
public class FileController {
    @Autowired
    private FileService fileService;
    @Autowired
    private OssService ossService;

    @Value("${minio.bucket}")
    private String bucket;
    @Value("${minio.chunk-bucket}")
    private String chunkBucket;
    @Value("${minio.endpoint}")
    private String endpoint;

    @PostMapping("/chunk")
    public R<FileVo> chunkUpload(@RequestBody FileDto dto) {
        // 根据文件MD5作为主键查询,查询文件是否上传过
        FileEntity fileEntity = fileService.getById(dto.getFileMd5());
        if (fileEntity != null) {
            // 文件已上传完成，实现秒传
            if (fileEntity.getFileState().equals(FileStateEnum.UPLOAD_SUCCESS.getCode())) {
                return R.ok(new FileVo()
                        .setUploadCode(fileEntity.getFileState())
                        .setFileUrl(fileEntity.getFileUrl()));
            }

            // 文件断点续传
            if (fileEntity.getFileState().equals(FileStateEnum.CHUNK_UPLOAD.getCode())) {
                return R.ok(this.breakpointResume(dto));
            }
        }
        FileVo upload = this.createUpload(dto);
        fileService.save(new FileEntity()
                .setFileName(dto.getFileName())
                .setId(dto.getFileMd5())
                .setFileState(FileStateEnum.CHUNK_UPLOAD.getCode()));
        return R.ok(upload);
    }

    @PostMapping("/compose")
    public R<FileVo> composeFile(@RequestBody FileDto dto) {
        // 分片文件
        List<String> chunks = ossService.listObjectNames(chunkBucket, dto.getFileMd5());

        if (!ossService.composeObject(chunkBucket, bucket, chunks, dto.getFileName())) {
            fileService.removeById(dto.getFileMd5());
            return R.failed("文件合并失败，重新上传");
        }
        String fileUrl = endpoint + bucket + "/" + dto.getFileName();
        // 更新数据库文件信息
        fileService.updateById(new FileEntity()
                .setId(dto.getFileMd5())
                .setFileState(FileStateEnum.UPLOAD_SUCCESS.getCode())
                .setFileName(dto.getFileName())
                .setFileUrl(endpoint + bucket + "/" + dto.getFileName()));
        return R.ok(new FileVo()
                .setFileUrl(fileUrl)
                .setUploadCode(FileStateEnum.UPLOAD_SUCCESS.getCode()));
    }


    /**
     * 断点续传
     *
     * @param dto
     * @return
     */
    private FileVo breakpointResume(FileDto dto) {
        // 已上传成功的分片
        Map<Integer, String> okChunkMap = ossService.mapChunkObjectNames(chunkBucket, dto.getFileMd5());
        // 没有分片记录创建分片上传
        if (okChunkMap == null) {
            return this.createUpload(dto);
        }
        List<UploadDto> chunkUploadUrls = new ArrayList<>();
        for (int i = 1; i <= dto.getChunkCount(); i++) {
            // 判断当前分片是否上传完成
            if (!okChunkMap.containsKey(i)) {
                chunkUploadUrls.add(new UploadDto()
                        .setPartNumber(i)
                        .setUploadUrl(ossService.createUploadChunkUrl(chunkBucket, dto.getFileMd5(), i)));
            }
        }
        // 分片全部上传完成
        if (chunkUploadUrls.size() == 0) {
            return new FileVo().setUploadCode(FileStateEnum.CHUNK_UPLOAD_SUCCESS.getCode());
        }

        return new FileVo()
                .setUploadCode(FileStateEnum.CHUNK_UPLOAD.getCode())
                .setChunkUploadUrls(chunkUploadUrls);
    }

    /**
     * 新建分片上传
     *
     * @param dto
     * @return
     */
    private FileVo createUpload(FileDto dto) {
        List<UploadDto> chunkUploadUrls = new ArrayList<>();
        for (int i = 1; i <= dto.getChunkCount(); i++) {
            chunkUploadUrls.add(new UploadDto()
                    .setPartNumber(i)
                    .setUploadUrl(ossService.createUploadChunkUrl(chunkBucket, dto.getFileMd5(), i)));
        }
        return new FileVo()
                .setUploadCode(FileStateEnum.CHUNK_UPLOAD.getCode())
                .setChunkUploadUrls(chunkUploadUrls);
    }
}
